package com.zhangsr.zus.config.api;

import com.zhangsr.zus.common.Constants;
import com.zhangsr.zus.common.URL;
import com.zhangsr.zus.common.Version;
import com.zhangsr.zus.common.utils.NetUtils;
import com.zhangsr.zus.rpc.api.Invoker;
import com.zhangsr.zus.rpc.api.RpcProtocol;
import com.zhangsr.zus.rpc.api.RpcProxyFactory;
import com.zhangsr.zus.rpc.api.proxy.jdk.JdkProxyFactory;
import com.zhangsr.zus.rpc.zus.ZusRpcProtocol;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: zhangsr
 */
public class ZusReferenceConfig<T> extends AbstractReferenceConfig {

    private static final Logger logger = Logger.getLogger(ZusServiceConfig.class);

    private static final RpcProtocol refProtocol = new ZusRpcProtocol();
    private static final RpcProxyFactory proxyFactory = new JdkProxyFactory();

    // 接口类型
    private String interfaceName;

    private Class<?> interfaceClass;

    private String protocol;

    // 点对点直连服务提供地址
    private String url;

    // 接口代理类引用
    private transient volatile T ref;

    private transient volatile Invoker<?> invoker;

    private transient volatile boolean initialized;

    private transient volatile boolean destroyed;

    private final List<URL> urls = new ArrayList<URL>();

    public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("Already destroyed!");
        }
        if (ref == null) {
            init();
        }
        return ref;
    }

    public void destroy() throws Exception {
    }


    private void init() {
        if (initialized) {
            return;
        }
        initialized = true;
        if (interfaceName == null || interfaceName.length() == 0) {
            throw new IllegalStateException("<zus:reference interface=\"\" /> interface not allow null!");
        }

        Map<String, String> map = new HashMap<String, String>();
        map.put(Constants.ZUS_VERSION_KEY, Version.getVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        map.put(Constants.INTERFACE_KEY, interfaceName);

        ref = createProxy(map);
    }

    private T createProxy(Map<String, String> map) {
        if (url != null && url.length() > 0) { // 用户指定URL，指定的URL可能是对点对直连地址，也可能是注册中心URL
            String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
            if (us != null && us.length > 0) {
                for (String u : us) {
                    URL url = URL.valueOf(u);
                    urls.add(url);
                }
            }
        } else {
            if (urls == null || urls.size() == 0) {
                throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use zus version " + Version.getVersion() + ", please config <zus:registry address=\"...\" /> to your spring config.");
            }
        }
        if (url != null && url.length() > 0) { // 用户指定URL，指定的URL是对点对直连地址
            invoker = refProtocol.refer(interfaceClass, urls.get(0));
        }
        Boolean c = check;
        if (c == null) {
            c = true; // default true
        }
        if (c) {
            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use zus version " + Version.getVersion());
        }
        if (logger.isInfoEnabled()) {
            logger.info("Refer zus service " + interfaceClass.getName() + " from url " + invoker.getUrl());
        }
        // 创建服务代理
        return (T) proxyFactory.getProxy(invoker);
    }

    public Class<?> getInterfaceClass() {
        if (interfaceClass != null) {
            return interfaceClass;
        }
        try {
            if (interfaceName != null && interfaceName.length() > 0) {
                this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
            }
        } catch (ClassNotFoundException t) {
            throw new IllegalStateException(t.getMessage(), t);
        }
        return interfaceClass;
    }

    /**
     * @param interfaceClass
     * @see #setInterface(Class)
     * @deprecated
     */
    @Deprecated
    public void setInterfaceClass(Class<?> interfaceClass) {
        setInterface(interfaceClass);
    }

    public String getInterface() {
        return interfaceName;
    }

    public void setInterface(Class<?> interfaceClass) {
        if (interfaceClass != null && !interfaceClass.isInterface()) {
            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
        }
        this.interfaceClass = interfaceClass;
        setInterface(interfaceClass == null ? (String) null : interfaceClass.getName());
    }

    public void setInterface(String interfaceName) {
        this.interfaceName = interfaceName;
        if (id == null || id.length() == 0) {
            id = interfaceName;
        }
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }
}
